import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;

public class cGame extends Canvas implements Runnable, Device{

    cGame()
    {
        state = State.init;
        setFullScreenMode(true);
    }

    public void drawString(String s, int x, int y, int anchor)
    {
        fuente.drawString(s.toUpperCase(), x, y, anchor);
    }

    public void drawSoftkey(String izq, String der)
    {
        if(izq != null)
        {
            drawString(izq, 2, alto - 2, Graphics.LEFT | Graphics.BOTTOM);
        }
        if(der != null)
        {
            drawString(der, ancho - 2, alto - 2, Graphics.RIGHT | Graphics.BOTTOM);
        }
    }

    public void keyReleased(int key)
    {
        keyReleased = (short) getKeyMask(key);
    }

    public void keyPressed(int key)
    {
    }

    public int getKeyMask(int key)
    {
        switch(key)
        {
            case Canvas.KEY_NUM0:
            case Canvas.KEY_NUM1:
            case Canvas.KEY_NUM2:
            case Canvas.KEY_NUM3:
            case Canvas.KEY_NUM4:
            case Canvas.KEY_NUM5:
            case Canvas.KEY_NUM6:
            case Canvas.KEY_NUM7:
            case Canvas.KEY_NUM8:
            case Canvas.KEY_NUM9:
                return key_0_mask + (key - Canvas.KEY_NUM0);
            default:
            {
                if(key == ski)
                    return key_ski_mask;
                else if(key == skd)
                    return key_skd_mask;
                else if(key == dpu)
                    return key_up_mask;
                else if(key == dpd)
                    return key_down_mask;
                else if(key == dpl)
                    return key_left_mask;
                else if(key == dpr)
                    return key_right_mask;
                else if(key == dpo)
                    return key_ok_mask;
            }
        }
        return -100;
    }

    public void paint(Graphics g)
    {
        try
        {
            _g = g;
            _g.setClip(0, 0, ancho, alto);
            switch(state)
            {
                case State.exit:
                {
                    break;
                }
                case State.init:
                {
                    paintInit(_g);
                    break;
                }
                case State.game:
                {
                    paintGame(_g);
                    break;
                }
                case State.menu:
                {
                    paintMenu(_g);
                    break;
                }
                case State.nivel:
                {
                    paintMenu(_g);
                    break;
                }
                case State.end:
                {
                    paintEndLevel(_g);
                    break;
                }
                case State.records:
                {
                    paintRecords(_g);
                    break;
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
            System.out.print("T-> exception en paint: " + e.getMessage());
        }
        drawSoftkey(skIzq, skDer);
    }

    public void update()
    {
        try
        {
            switch(state)
            {
                case State.exit:
                {
                    break;
                }
                case State.init:
                {
                    updateInit();
                    break;
                }
                case State.game:
                {
                    updateGame();
                    break;
                }
                case State.menu:
                {
                    updateMenu();
                    break;
                }
                case State.nivel:
                {
                    updateMenu();
                    break;
                }
                case State.end:
                {
                    updateEndLevel();
                    break;
                }
                case State.records:
                {
                    updateRecords();
                    break;
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
            System.out.print("T-> exception en update: " + e.getMessage());
        }
        keyReleased = -100;
    }

    public void run()
    {
        while(state != State.exit)
        {
            update();
            repaint();
            serviceRepaints();
            try
            {
                Thread.sleep(50);
            }
            catch(Exception e)
            {
            }
        }
        try
        {
            Midlet.midletInstance.notifyDestroyed();
        }
        catch(Exception e)
        {
        }
    }

    void paintEndLevel(Graphics g)
    {
        g.setColor(0x000000);
        g.fillRect(0, 0, ancho, alto);
        drawString("Has finalizado", ancho >> 1, (alto >> 1) - 30, Graphics.HCENTER | Graphics.TOP);
        drawString("el nivel en", ancho >> 1, (alto >> 1) - 10, Graphics.HCENTER | Graphics.TOP);
        int segundos = (int) ((time_init / 1000) % 60);
        int minutos = (int) (time_init / 60000);
        String time = (minutos < 10 ? "0" : "") + minutos + ":" + (segundos < 10 ? "0" : "") + segundos;
        drawString("" + time, ancho >> 1, (alto >> 1) +10, Graphics.HCENTER | Graphics.TOP);
    }

    void paintGame(Graphics g)
    {
        g.setColor(0xAEAAEA);
        g.fillRect(0, 0, ancho, alto);
        fuente.setCurrentPalette(1);
        for(short x=0; x < matriz.length; x++)
        {
            for(short y=0; y < matriz[0].length; y++)
            {
                g.setColor(matriz_inicial[x][y] ? 0x999999 : 0x00FF00);
                g.fillRect(celdaX + x * anchoCelda, celdaY + y * altoCelda, anchoCelda, altoCelda);
                g.setColor(0x000000);
                g.drawRect(celdaX + x * anchoCelda, celdaY + y * altoCelda, anchoCelda, altoCelda);
                if(matriz[x][y] != 0)
                {
                    drawString("" + matriz[x][y], celdaX + (int)((x + 0.5) * anchoCelda), celdaY + (int)((y + 0.5) * altoCelda), 0);
                }
            }
        }
        g.setColor(0xDDDDDD);
        for(short x=0; x < matriz.length; x += 3)
        {
            for(short y=0; y < matriz[0].length; y += 3)
            {
                g.drawRect(celdaX + x * anchoCelda - 1, celdaY + y * altoCelda - 1, 3 * anchoCelda + 2, 3 * altoCelda + 2);
                g.drawRect(celdaX + x * anchoCelda, celdaY + y * altoCelda, 3 * anchoCelda, 3 * altoCelda);
                g.drawRect(celdaX + x * anchoCelda + 1, celdaY + y * altoCelda + 1, 3 * anchoCelda - 2, 3 * altoCelda + 2);
            }
        }

        g.setColor(0xFF0000);
        g.drawRect(celdaX + cursor_x * anchoCelda + 1, celdaY + cursor_y * altoCelda + 1, anchoCelda - 2, altoCelda - 2);
        g.drawRect(celdaX + cursor_x * anchoCelda, celdaY + cursor_y * altoCelda, anchoCelda, altoCelda);
        g.drawRect(celdaX + cursor_x * anchoCelda - 1, celdaY + cursor_y * altoCelda - 1, anchoCelda + 2, altoCelda + 2);

        for(int i=0; i < 9; i++)
        {
            g.setColor(0x0000FF);
            g.fillRect(barra_x + i * ancho_barra, barra_y - alto_barra * matriz_numeros[i], ancho_barra, alto_barra * matriz_numeros[i]);
            g.setColor(0xFFFFFF);
            g.drawRect(barra_x + i * ancho_barra, barra_y - alto_barra * matriz_numeros[i], ancho_barra, alto_barra * matriz_numeros[i]);
        }

        time_init += System.currentTimeMillis() - last_paint;
        last_paint = System.currentTimeMillis();
        int segundos = (int) ((time_init / 1000) % 60);
        int minutos = (int) (time_init / 60000);
        String time = (minutos < 10 ? "0" : "") + minutos + ":" + (segundos < 10 ? "0" : "") + segundos;
        drawString("" + time, ancho >> 1, time_y, Graphics.HCENTER | Graphics.TOP);
        fuente.setCurrentPalette(0);
    }
ASprite p;
    void paintInit(Graphics g)
    {
        g.setColor(0xABCDEF);
        g.fillRect(0, 0, ancho, alto);
    }

    void paintMenu(Graphics g)
    {
        g.setColor(0xffffff);
        g.fillRect(0, 0, ancho, alto);
//        drawString("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10, 10, 0);
//        drawString(" 0123456789:", 10, 30, 0);
        short cantidad = 0;
        String menu[] = null;
        if(menu_id == Menu.menu_id)
        {
            cantidad = Menu.cantidad;
            menu = Textos.MainMenu[lenguaje];
        }
        else if(menu_id == Igm.menu_id)
        {
            cantidad = Igm.cantidad;
            menu = Textos.IgmMenu[lenguaje];
        }
        else if(menu_id == Nivel.menu_id)
        {
            cantidad = Nivel.cantidad;
            menu = Textos.Nivel[lenguaje];
        }
        short menu_y = (short) ((alto - cantidad * menu_h) >> 1);
        for(int i = 0; i < cantidad; i++)
        {
            if(optionSelected == i)
            {
                g.setColor(0xFEAFEA);
                g.fillRect(10, menu_y - 5 + i * menu_h, ancho - 20, menu_h);
                fuente.setCurrentPalette(1);
            }
            else
            {
                fuente.setCurrentPalette(0);
            }
            drawString(menu[i], ancho >> 1, menu_y + i * menu_h, Graphics.HCENTER | Graphics.TOP);
        }
        p.paintModule(g, 0, 15, 15, 0);
    }

    void paintRecords(Graphics g)
    {
        g.setColor(0x000000);
        g.fillRect(0, 0, ancho, alto);
        int sep = (alto - 30) / (rms_posiciones + 2);
        int segundos;
        int minutos;
        String time;
        for(int i=0; i<rms_posiciones; i++)
        {
            segundos = records[i][rms_segundos];
            minutos = records[i][rms_minutos];
            time = (minutos < 10 ? "0" : "") + minutos + ":" + (segundos < 10 ? "0" : "") + segundos;
            drawString(time, (ancho>>1) - 20, 30 + sep * i, 0);
        }
    }

    public int random(int k, int m)
    {
        int r1 = 5;
        int r2 = 9;

        Random rnd = new Random();

        int b = ((r1 * r2 + (rnd.nextInt() * k * 7)) % (k - 1));
        System.out.println("T-> b: " + b);
        b = Math.abs(((int) (b / m)) * m);
        System.out.println("T-> b: " + b);
        return b;
    }

    final static byte rms_record = 0;
    final static byte rms_minutos   = 0;
    final static byte rms_segundos  = rms_minutos + 1;
    final static byte rms_total     = rms_segundos + 1;
    final static byte rms_posiciones = 10;
    int[][] records;
    void rms(boolean save)
    {
        byte[] dato = null;
        int offset = 0;

        if(!save)
        {
            records = new int[rms_posiciones][rms_total];
        }
        try
        {
            RecordStore recordStore = RecordStore.openRecordStore("goluku",true);
            if(save)
            {
                dato = new byte[rms_total * rms_posiciones];
                for(int i=0; i < rms_posiciones; i++)
                {
                    dato[offset++] = (byte)records[i][rms_minutos];
                    dato[offset++] = (byte)records[i][rms_segundos];
                    System.out.println("T-> " + records[i][rms_minutos] + ":" + records[i][rms_segundos]);
                }
                recordStore.addRecord(dato, 0, dato.length);
            }
            else
            {
                System.out.println("T-> records: " + recordStore.getNumRecords());
                dato = new byte[rms_total * rms_posiciones];
                recordStore.getRecord(1, dato, 0);
                for(int i=0; i < rms_posiciones; i++)
                {
                    recordStore.getRecord(i + 1, dato, 0);
                    records[i][rms_minutos] = dato[offset++];
                    records[i][rms_segundos] = dato[offset++];
                    System.out.println("T-> " + records[i][rms_minutos] + ":" + records[i][rms_segundos]);
                }
                System.out.println();
            }
            recordStore.closeRecordStore();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    void updateEndLevel()
    {
        skIzq = null;
        skDer = null;
        if(keyReleased != -100)
        {
            System.out.println("T-> endGame");
            state = State.menu;
            menu_id = Menu.menu_id;
            optionSelected = 0;
            int segundos = (int) ((time_init / 1000) % 60);
            int minutos = (int) (time_init / 60000);
            for(int i=0; i<rms_posiciones; i++)
            {
                if((minutos < records[i][rms_minutos] && segundos < records[i][rms_segundos])
                        || (records[i][rms_minutos] == 0 && records[i][rms_segundos] == 0))
                {
                    System.out.println("T-> " + records[i][rms_minutos] + ":" + records[i][rms_segundos]);
                    records[i][rms_minutos] = minutos;
                    records[i][rms_segundos] = segundos;
                    System.out.println("T-> " + records[i][rms_minutos] + ":" + records[i][rms_segundos]);
                    for(int j=i+1; j<rms_posiciones - 1; j++)
                    {
                        records[j + 1][rms_minutos] = records[j][rms_minutos];
                        records[j + 1][rms_segundos] = records[j][rms_segundos];
                    }
                    break;
                }
            }
            rms(true);
        }
    }

    void updateGame()
    {
        skIzq = Textos.Softkeys[lenguaje][Textos.softkey_menu];
        skDer = null;
        switch(keyReleased)
        {
            case key_0_mask:
            {
                if(!matriz_inicial[cursor_x][cursor_y])
                {
                    if(matriz[cursor_x][cursor_y] != 0)
                    {
                        matriz_numeros[matriz[cursor_x][cursor_y]-1]++;
                    }
                    matriz[cursor_x][cursor_y] = 0;
                    numeros--;
                }
                break;
            }
            case key_1_mask:
            case key_2_mask:
            case key_3_mask:
            case key_4_mask:
            case key_5_mask:
            case key_6_mask:
            case key_7_mask:
            case key_8_mask:
            case key_9_mask:
            {
                if(validarCelda(keyReleased - 1) && numeros == 81)
                {
                    state = State.end;
                }
                break;
            }
            case key_left_mask:
            {
                cursor_x--;
                if(cursor_x == -1)
                {
                    cursor_x = 8;
                }
                break;
            }
            case key_right_mask:
            {
                cursor_x++;
                if(cursor_x == 9)
                {
                    cursor_x = 0;
                }
                break;
            }
            case key_up_mask:
            {
                cursor_y--;
                if(cursor_y == -1)
                {
                    cursor_y = 8;
                }
                break;
            }
            case key_down_mask:
            {
                cursor_y++;
                if(cursor_y == 9)
                {
                    cursor_y = 0;
                }
                break;
            }
            case key_ok_mask:
            {
                break;
            }
            case key_skd_mask:
            {
                break;
            }
            case key_ski_mask:
            {
                state = State.menu;
                menu_id = Igm.menu_id;
                optionSelected = 0;
                break;
            }
        }
    }

    void updateInit()
    {
        state = State.menu;
        menu_id = Menu.menu_id;
        lenguaje = Textos.language_español;
        rms(false);
        fuente = new Sprite(_g);
        try
        {
            for(int i = -256; i < 256; i++)
            {
                String key = null;
                try
                {
                    key = getKeyName(-i);
                    System.out.println("T-> " + i + ": " + key);
                }
                catch(Exception e)
                {
//                    System.out.println("T-> excepcion en getKeyName " + e.getMessage());
                }
                if(key == null)
                {
                    continue;
                }
                else if (key.indexOf("UP") >= 0)
                {
                    dpu = (byte) -i;
                }
                else if (key.indexOf("DOWN") >= 0)
                {
                    dpd = (byte) -i;
                }
                else if (key.indexOf("LEFT") >= 0)
                {
                    dpl = (byte) -i;
                }
                else if (key.indexOf("RIGHT") >= 0)
                {
                    dpr = (byte) -i;
                }
                else if (key.indexOf("SELECT") >= 0)
                {
                    dpo = (byte) -i;
                }
                else if (key.indexOf("SOFT") >= 0)
                {
                    if (key.indexOf("1") > 0)
                    {
                        ski = (byte) -i;
                    }
                    else if (key.indexOf("2") > 0)
                    {
                        skd = (byte) -i;
                    }
                }
                else
                {
                    System.out.println("T-> no mapped " + i + ": " + key);
                }
            }
        }
        catch(Exception e)
        {
            System.out.println("E-> excepcion en getKeyName " + e.getMessage());
        }
        try
        {
            InputStream array = "".getClass().getResourceAsStream("/export.bsprite");
            byte[] b = new byte[56];
            System.out.println("D-> array: " + b.length);
            int offset = 0;
            while (offset < b.length)
            {
                b[offset++] = (byte)array.read();
            }
            System.out.println("D-> offset: " + offset);
            p = new ASprite();
            p.loadSprite(b);
        }
        catch(Exception e)
        {
            System.out.println("E-> excepcion loading sprite " + e.getMessage());
            e.printStackTrace();
        }
    }

    private void updateMenu()
    {
        short cantidad = 0;
        if(menu_id == Menu.menu_id)
        {
            cantidad = Menu.cantidad;
            skIzq = Textos.Softkeys[lenguaje][Textos.softkey_select];
            skDer = null;
        }
        else if(menu_id == Igm.menu_id)
        {
            cantidad = Igm.cantidad;
            skIzq = Textos.Softkeys[lenguaje][Textos.softkey_select];
            skDer = Textos.Softkeys[lenguaje][Textos.softkey_back];
        }
        else if(menu_id == Nivel.menu_id)
        {
            cantidad = Igm.cantidad;
            skIzq = Textos.Softkeys[lenguaje][Textos.softkey_select];
            skDer = Textos.Softkeys[lenguaje][Textos.softkey_back];
        }
        switch(keyReleased)
        {
            case key_2_mask:
            case key_up_mask:
            {
                optionSelected--;
                if(optionSelected == -1)
                {
                    optionSelected = (short) (cantidad - 1);
                }
                break;
            }
            case key_8_mask:
            case key_down_mask:
            {
                optionSelected++;
                if(optionSelected == cantidad)
                {
                    optionSelected = 0;
                }
                break;
            }
            case key_ski_mask:
            case key_5_mask:
            case key_ok_mask:
            {
                if(skIzq == null && keyReleased == key_ski_mask)
                {
                    break;
                }
                if(menu_id == Menu.menu_id)
                {
                    switch(optionSelected)
                    {
                        case Menu.jugar:
                            state = State.menu;
                            menu_id = Nivel.menu_id;
                            break;
                        case Menu.opciones:
                            break;
                        case Menu.records:
                            state = State.records;
                            break;
                        case Menu.salir:
                            state = State.exit;
                            break;
                    }
                }
                else if(menu_id == Igm.menu_id)
                {
                    switch(optionSelected)
                    {
                        case Igm.resumir:
                            state = State.game;
                            last_paint = System.currentTimeMillis();
                            break;
                        case Igm.main_menu:
                            state = State.menu;
                            menu_id = Menu.menu_id;
                            optionSelected = 0;
                            break;
                        case Igm.salir:
                            state = State.exit;
                            break;
                    }
                }
                else if(menu_id == Nivel.menu_id)
                {
                    numeros = 0;
                    state = State.game;
                    dificultad = optionSelected;
                    matriz = new int[9][9];
                    matriz_inicial = new boolean[9][9];
                    matriz_numeros = new int[9];
                    int tab = random(Tableros.tableros[dificultad].length, 2);
                    for(short i=0; i < matriz_numeros.length; i++)
                    {
                        matriz_numeros[i] = 9;
                    }
                    for(short x=0; x < matriz.length; x++)
                    {
                        for(short y=0; y < matriz[0].length; y++)
                        {
                            matriz[x][y] = Tableros.tableros[dificultad][tab].charAt(y * 9 + x) - 48;
                            matriz_inicial[x][y] = false;
                            if(matriz[x][y] != 0)
                            {
                                matriz_inicial[x][y] = true;
                                matriz_numeros[matriz[x][y]-1]--;
                                numeros++;
                            }
                        }
                    }
                    optionSelected = 0;
                    cursor_x = 4;
                    cursor_y = 4;
                    last_paint = System.currentTimeMillis();
                    time_init = 0;
                }
                break;
            }
            case key_skd_mask:
            {
                if(skIzq == null)
                {
                    break;
                }
                if(menu_id == Igm.menu_id)
                {
                    state = State.game;
                    last_paint = System.currentTimeMillis();
                }
                else if(menu_id == Nivel.menu_id)
                {
                    state = State.menu;
                    menu_id = Menu.menu_id;
                    optionSelected = 0;
                }
                break;
            }
        }
    }

    void updateRecords()
    {
        skIzq = null;
        skDer = Textos.Softkeys[lenguaje][Textos.softkey_back];
        if(keyReleased == key_skd_mask)
        {
            state = State.menu;
            menu_id = Menu.menu_id;
            optionSelected = 0;
        }
    }

    private boolean validarCelda(int numero)
    {
        if(matriz[cursor_x][cursor_y] != 0 || matriz_inicial[cursor_x][cursor_y])
        {
            return false;
        }
        for(int i = 0; i < matriz.length; i++)
        {
            if(numero == matriz[i][cursor_y])
            {
                return false;
            }
        }
        for(int i = 0; i < matriz.length; i++)
        {
            if(numero == matriz[cursor_x][i])
            {
                return false;
            }
        }
        short inicial_x = 0;
        short inicial_y = 0;
        if(cursor_x < 3)
            inicial_x = 0;
        else if(cursor_x < 6)
            inicial_x = 3;
        else
            inicial_x = 6;
        if(cursor_y < 3)
            inicial_y = 0;
        else if(cursor_y < 6)
            inicial_y = 3;
        else
            inicial_y = 6;
        for(int x = inicial_x; x < inicial_x + 3; x++)
        {
            for(int y = inicial_y; y < inicial_y + 3; y++)
            {
                if(numero == matriz[x][y])
                {
                    return false;
                }
            }
        }
        matriz[cursor_x][cursor_y] = numero;
        matriz_numeros[numero-1]--;
        numeros++;
        return true;
    }

    short state;
    int[][] matriz;
    boolean[][] matriz_inicial;
    short cursor_x = 4;
    short cursor_y = 4;
    short dificultad = 0;
    short keyReleased = -1;
    short optionSelected = 0;
    short numeros = 0;
    int[] matriz_numeros;
    byte menu_id = 0;
    long time_init = 0;
    long last_paint = 0;
    private Graphics _g;
    String skIzq = null;
    String skDer = null;

    short lenguaje;

    Sprite fuente;

    final int key_0_mask = 1;
    final int key_1_mask = 2;
    final int key_2_mask = 3;
    final int key_3_mask = 4;
    final int key_4_mask = 5;
    final int key_5_mask = 6;
    final int key_6_mask = 7;
    final int key_7_mask = 8;
    final int key_8_mask = 9;
    final int key_9_mask = 10;
    final int key_up_mask = 11;
    final int key_down_mask = 12;
    final int key_left_mask = 13;
    final int key_right_mask = 14;
    final int key_ok_mask = 15;
    final int key_ski_mask = 16;
    final int key_skd_mask = 17;

    short ski = -99;
    short skd = -99;
    short dpu = -1;
    short dpd = -2;
    short dpl = -3;
    short dpr = -4;
    short dpo = -5;
}
